package vistop.ui;

import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;

import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
import javax.media.opengl.glu.GLUtessellator;
import javax.swing.JPanel;

import vistop.datatypes.Polygon;
import vistop.datatypes.Vertex;

import com.jogamp.opengl.util.gl2.GLUT;

public class JoglPanel extends JPanel implements GLEventListener {
	
	private Dimension canvasSize;
	private int camRotX;
	private int camRotY;
	private float camDistance;
	private final float INIT_CAM_DISTANCE = 20; //50;
	private List<Polygon> polygons;
	
	public JoglPanel(UserInterface ui) {
		this.polygons = ui.getPolygons();
		canvasSize = ui.getCanvasSize();
		camRotX = 0;
		camRotY = 0;
		camDistance = INIT_CAM_DISTANCE;
	}
	
	@Override
	public void display(GLAutoDrawable drawable) {
		update();
		GLU glu = new GLU();
		setCamera(drawable, glu, INIT_CAM_DISTANCE);
		prepareForRedering(drawable);
		GL2 gl = drawable.getGL().getGL2();
		gl.glClear (GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
		TessellCallBack tessCallback = new TessellCallBack(gl, glu);
		renderPolygon(gl, tessCallback);
	}
	
	private void prepareForRedering (GLAutoDrawable drawable) {
		GL2 gl = drawable.getGL().getGL2();
	    //gl.glClearColor(0.694f, 0.875f, 0.953f, 1f);
		gl.glClearColor(0f, 0f, 0f, 1f);
	    gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
	    
	    // Set Ambient Lighting To Fairly Dark Light (No Color)
        // float[] global_ambient = new float[]{0.3f, 0.3f, 0.3f, 1.0f};    
        // Set The Light Position
	    //float[] light0pos = new float[]{0.0f, 5.0f, 10.0f, 1.0f};
	    float[] light0pos = new float[]{-10f, -10f, 7f, 1.0f};
        // More Ambient Light
        //float[] light0ambient = new float[]{0.2f, 0.2f, 0.2f, 1.0f};
	    float[] light0ambient = new float[]{0.1f, 0.1f, 0.1f, 1f};
        // Set The Diffuse Light A Bit Brighter
        //float[] light0diffuse = new float[]{0.4f, 0.4f, 0.4f, 1.0f};    
        float[] light0diffuse = new float[]{0.2f, 0.2f, 0.2f, 1f};    

        // Fairly Bright Specular Lighting
        //float[] light0specular = new float[]{0.8f, 0.8f, 0.8f, 1.0f};    
        float[] light0specular = new float[]{1f, 1f, 1f, 1f};    


        // And More Ambient Light
        // float[] lmodel_ambient = new float[]{0.2f, 0.2f, 0.2f, 1.0f};      
        // Set The Lights Position
        gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, light0pos, 0);        
        // Set The Ambient Light
        gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_AMBIENT, light0ambient, 0);      
        // Set The Diffuse Light
        gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_DIFFUSE, light0diffuse, 0);      
        // Set Up Specular Lighting
        gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_SPECULAR, light0specular, 0);      
        gl.glEnable(GL2.GL_LIGHTING);  // Enable Lighting
        gl.glEnable(GL2.GL_LIGHT0);  // Enable Light0

        gl.glEnable(GL2.GL_DEPTH_TEST);
        gl.glEnable(GL2.GL_BLEND_SRC_ALPHA); //GL2.GL_BLEND_SRC_ALPHA, or GL2.GL_BLEND
        gl.glShadeModel(GL2.GL_SMOOTH); // GL_FLAT or GL_SMOOTH
        gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2.GL_SPECULAR, 
                new float[] { 0.7f, 0.7f, 0f, 1 },  0);
	}
	
	private void renderPolygon(GL2 gl, TessellCallBack tessCallback) {
		
		GLUtessellator tobj = GLU.gluNewTess();
		
		GLU.gluTessCallback(tobj, GLU.GLU_TESS_VERTEX, tessCallback);// glVertex3dv;
		GLU.gluTessCallback(tobj, GLU.GLU_TESS_BEGIN, tessCallback);// beginCallback;
		GLU.gluTessCallback(tobj, GLU.GLU_TESS_END, tessCallback);// endCallback;
		GLU.gluTessCallback(tobj, GLU.GLU_TESS_ERROR, tessCallback);// errorCallback;
				
		for (Polygon p : polygons) {
			GLU.gluTessBeginPolygon(tobj, null);
			for (List<Vertex> vs : p.getPolygonComponentsVertex()) {
				GLU.gluTessBeginContour(tobj);
				for (int i = vs.size() -1; i >= 0; i --) {
					Vertex v = vs.get(i);
					GLU.gluTessVertex(tobj, v.getCoord(), 0, v.getCoord());
				}
				GLU.gluTessEndContour(tobj);
			}
			GLU.gluTessEndPolygon(tobj);
		}
		
		GLU.gluDeleteTess(tobj);
	}
	
	private void update() {
		
	}

	@Override
	public void dispose(GLAutoDrawable arg0) {
		// TODO Auto-generated method stub
	
	}

	@Override
	public void init(GLAutoDrawable drawable) {
		drawable.getGL().setSwapInterval(1);
	}

	@Override
	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        GL2 gl = drawable.getGL().getGL2();
        gl.glViewport(0, 0, width, height);
    }
		
	private void setCamera(GLAutoDrawable drawable, GLU glu, float distance) {
        // Change to projection matrix.
		GL2 gl = drawable.getGL().getGL2();
        gl.glMatrixMode(GL2.GL_PROJECTION);
        gl.glLoadIdentity();

        // Perspective.
        float widthHeightRatio = canvasSize.width / canvasSize.height;
        if (widthHeightRatio == 0) {
        	widthHeightRatio = 1.5f;
        }
        glu.gluPerspective(45, widthHeightRatio, 1, 1000);
        glu.gluLookAt(0, 0, camDistance, 0, 0, 0, 0, 1, 0);
        gl.glRotatef(camRotX, 1, 0, 0);
        gl.glRotatef(camRotY, 0, 1, 0);

        // Change back to model view matrix.
        gl.glMatrixMode(GL2.GL_MODELVIEW);
        gl.glLoadIdentity();
    }
	
	public void rotateCamera(int degX, int degY) {
		camRotX += 3*degX;
		camRotY += 3*degY;
	}
	
	/**
	 * Zooms the camera away
	 * @param zoom positive number zooms in, negative zooms out
	 */
	public void zoomCamera(int zoom) {
		if (zoom >=0) 
			camDistance -= 0.5f;
		else {
			camDistance += 0.5;
		}
	}
	
	public void resetCamPos() {
		camRotX = 0;
		camRotY = 0;
		camDistance = INIT_CAM_DISTANCE;
	}
}